
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
//Android,iOS,mac
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';

//import 'package:flutter_inappwebview/flutter_inappwebview.dart';

//windows
import 'package:webview_windows/webview_windows.dart';

//mac linux(弹出新窗口)
import 'package:desktop_webview_window/desktop_webview_window.dart' as desktopWebview;
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:window_manager/window_manager.dart';
import 'package:xsdk_flutter/xsdk/framework/page/x_page_state.dart';
import 'package:xsdk_flutter/xsdk/operation/x_constant_temp.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_date.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_flutter.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_log.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_string.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_widget.dart';

/*
webview:

1.1.适用: android iOS   https://pub.flutter-io.cn/packages/webview_flutter
注意: js调用原生不能直接返回结果,需要原生调用js传值
dependencies:
  webview_flutter: ^4.0.2
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';


注意: webview_flutter插件主要是用来加载和显示web内容的，它本身并不提供直接选择文件或图片的接口。
如果你需要在WebView中选择文件或图片，你可能需要使用flutter_inappwebview插件，这个插件提供了更多的功能，包括文件选择功能。


1.2 适用: android iOS https://pub-web.flutter-io.cn/packages/flutter_inappwebview/install
dependencies:
  flutter_inappwebview: ^6.1.5
import 'package:flutter_inappwebview/flutter_inappwebview.dart';


2.适用: windows10+  https://pub.flutter-io.cn/packages/webview_windows
dependencies:
  webview_windows: ^0.4.0
import 'package:webview_windows/webview_windows.dart';

注意: 不要使用webview_cef,实测影响使用webview_windows编译通过,冲突??,第一次编译需下载200m,等待时间长)
3.适用: mac,windows,linux(弹出独立新窗口)
https://pub.flutter-io.cn/packages/desktop_webview_window
dependencies:
  desktop_webview_window: ^0.1.6
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
使用:
linux requirement:  sudo apt install webkit2gtk-4.0


4.适用:web (注意:实测与webview_windows冲突,不能同时使用; 不显示网页按f12查看报错,如:跨域问题) https://pub.flutter-io.cn/packages/webview_flutter_web
dependencies:
  webview_flutter_web: ^0.2.1
import 'package:webview_flutter_web/webview_flutter_web.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';


#窗口管理 windows mac linux   https://pub.flutter-io.cn/packages/window_manager
dependencies:
  window_manager: ^0.3.0
import 'package:window_manager/window_manager.dart';


https://www.jianshu.com/p/02d24f12fe23

html widget core: https://pub.flutter-io.cn/packages/flutter_widget_from_html_core


//1.加载http白屏问题
android在清单配置文件中添加:
<application
  ...
  android:networkSecurityConfig="@xml/network_security_config"
>

iOS在Runner/info.plist中添加对HTTP的信任,https和http混合模式:
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSAllowsArbitraryLoadsInWebContent</key>
    <true/>
</dict>
<key>io.flutter.embedded_views_preview</key>
<true/>

//2.允许在https中访问http,开启混合模式/////////////
android:
https://blog.csdn.net/wan903531306/article/details/129862208
https://huaweicloud.csdn.net/64ed8f33a3cccc782cc571e4.html
Android 找到External Libraries -> Flutter Plugins -> webview_flutter_android_xxx -> android -> WebSettingHostApiImpl.java
在create()方法中添加:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
      webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
注意此方法为修改本地缓存代码,如果更新版本会清除修改代码


 */
abstract class XPageWebViewState extends XPageState {
  String desktopNewWindowTitle;
  Widget? desktopIndexPageCenterWidget;

  /*
  适用android,iOS
  {
  'domain':domain,
  'cookies':{...}
  }
   */
  Map<String,dynamic>? cookiesMap;
  bool isCleanAllCookies;//是否清除所有已存在的cookies

  bool isFinishOverrideWebviewGoback;
  bool openDevTools;//是否打开开发者调试
  //bool isUseInappwebview;//是否使用flutter_inappwebview

  String? loadHtmlText;


  /*
  目前xsdk默认使用webview_flutter 4.7.0版本,适配旧android开发环境 gradle7.5,适配旧android开发项目(硬件连接,商米等旧第三方库)
  使用webview_flutter 4.10.0, 解决iOS 18.2+的触碰问题, 支持mac平台不需要新窗口打开,
  注意:android需要升级到gradle8.5+(旧项目有很多适配问题),在开发项目中配置webview_flutter 4.10.0依赖
   */
  bool isUseWebview_flutter_4_10_0=false;

  XPageWebViewState(
      {
        super.isKeepPageState = false,
        super.xTag,
        this.desktopNewWindowTitle = 'desktopNewWindowTitle',
        this.desktopIndexPageCenterWidget,
        this.cookiesMap,
        this.isCleanAllCookies=false,
        this.openDevTools=false,
        this.isFinishOverrideWebviewGoback=false,//finish方法替换为webview的goback方法
        this.loadHtmlText,
        this.isUseWebview_flutter_4_10_0=false,//在开发项目中,mac iOS平台编译,重新设置依赖:webview_flutter 4.10.0
      }
      );

  @override
  void finishPage({result, String? backToPointPageName}) {
    if(isFinishOverrideWebviewGoback){
      goBack();
    }else{
      super.finishPage(result:result, backToPointPageName:backToPointPageName);
    }
  }


  @override
  void titleBarLeftWidgetOnPressed() {
    //重写标题左侧按钮返回按键
    if(isFinishOverrideWebviewGoback){
      //返回上一页
      XFlutterUtils.finishPage(context);
    }else{
      //
      super.titleBarLeftWidgetOnPressed();
    }
  }


  @override
  void onCreate() {
    super.onCreate();

    webViewWidget=null;
    //init webview
    if (XFlutterUtils.isAndroidOriOSSystem()
        || (isUseWebview_flutter_4_10_0 && XFlutterUtils.isMacOS())
    ) {
      //android ios mac
      _initWebView_android_iOS();
    } else if (XFlutterUtils.isWindows()) {
      //window
      _initWebView_windows();
    } else{
      //mac/linux (弹出新窗口) webview_flutter 4.7.0不支持mac平台
      _initWebView_desktop_mac_linux();
    }

  }

  @override
  Future<void> updateData({int pageIndex = 1}) async {}

  @override
  Widget getBodyWidget(BuildContext context) {

    return XWidgetUtils.getLayoutStack(
      alignment:AlignmentDirectional.center,
      children: [
        XWidgetUtils.getLayoutContainer(
          padding: getWebViewPadding(),
          child: _getWebviewWidget(),
        ),
        XWidgetUtils.getLayoutVisibility(
          isVisible:isWebViewLoadStartShowCustomWidget,
          child: getUrlLoadStartCustomWidget(),
        ),
        XWidgetUtils.getLayoutVisibility(
          isVisible:isWebViewLoadFailShowCustomWidget,
          child: getUrlLoadFailCustomWidget(),
        ),
      ],
    );

  }

  EdgeInsets getWebViewPadding(){
    return XWidgetUtils.getEdgeInsets(0, 0, 0, 0);
  }

  //加载失败,是否显示自定义提示widget,注意: 只支持android,iOS,windows, 不支持mac(api没有失败通知);
  bool isWebViewLoadFailShowCustomWidget=false;
  //可重写自定义提示界面
  Widget getUrlLoadFailCustomWidget(){
    return XWidgetUtils.getLayoutContainer();
  }

  //是否加载开始时显示自定义widget
  bool isWebViewLoadStartShowCustomWidget=false;
  //可重写自定义提示界面
  Widget getUrlLoadStartCustomWidget(){
    return XWidgetUtils.getLayoutContainer();
  }

  @required
  String getWebUrl();

  @override
  void onDestroy() {
    super.onDestroy();
    if(isCleanAllCookies){
      cleanWebView();
    }
  }

  //
  Future<void> cleanWebView() async {
    if (XFlutterUtils.isAndroidOriOSSystem()
        || (isUseWebview_flutter_4_10_0 && XFlutterUtils.isMacOS())
    ) {
      await _clearWebView_android_iOS();
    } else if ('Windows' == XFlutterUtils.getSystemName()) {
      await _clearWebView_windows();
    } else{
      //mac linux
      await _clearWebView_desktop_mac_linux();
    }
  }


  //js交互////////////////////////////////////////////////////////////////////////////

  /*
  原生调用js方法 ok:android,windows
  flutter端: _controller.executeScript("jsFunctionTest('参数')");
  web端:
  function jsFunctionTest(param){
    console.log(param)
  }
   */
  Future<void> runJSFunction(String javaScript) async {
    if (XFlutterUtils.isAndroidOriOSSystem()
        || (isUseWebview_flutter_4_10_0 && XFlutterUtils.isMacOS())
    ) {
      //webview_flutter
      androidiOSWebViewController?.runJavaScript(javaScript);

    } else if ('Windows' == XFlutterUtils.getSystemName()) {
      /*
      webview_windows
      另外一种方式:可通过flutter端发送消息至web端实现,只使用window
      flutter端(发送):
      _controller.postWebMessage(json.encode([1,2,3]));
      web端(接收):
      window.chrome.webview.addEventListener('message',(e)=>{
        console.log(e)
      },false);

      windowWebViewController?.postWebMessage(msg);
       */
      windowWebViewController?.executeScript(javaScript);
    } else{
      //mac linux 使用desktop_webview_window
      dWebview?.evaluateJavaScript(javaScript);
    }
  }

  //原生调用js方法获得返回值 ok:android,windows
  Future<dynamic> runJSFunctionForResult(String javaScript) async {
    if (XFlutterUtils.isAndroidOriOSSystem()
        || (isUseWebview_flutter_4_10_0 && XFlutterUtils.isMacOS())
    ) {
      //webview_flutter
      androidiOSWebViewController
          ?.runJavaScriptReturningResult(javaScript)
          .then((dynamic value) {
        XLogUtils.printLog('---run js get result= $value');
        return value;
      });

    } else if ('Windows' == XFlutterUtils.getSystemName()) {
      //webview_windows
      windowWebViewController?.executeScript(javaScript).then((dynamic value) {
        XLogUtils.printLog('---run js get result= $value');
        return value;
      });
    } else{
      //mac linux 使用desktop_webview_window
      dWebview?.evaluateJavaScript(javaScript).then((String? value) {
        XLogUtils.printLog('---run js get result= $value');
      });
    }
  }

  /*
  设置js调用原生方法通用通道
  添加js调用原生方法 (web端入参约定:只能是1个string类型的json数据,不能直接传json类型),ok:windows,android,ios
  注意: 由于windows版本的webview与js通信方式原因,没有方法的name指定,因此全部平台都通过入参(1个String类型的json中的数据判断各个方法);
  且android/ios版本需要在加载url前设置好通道,因此可以是固定1个通用通道即可;

  web端的js调用原生通用方法:
  入参约定只有1个字符串类型的json数据,格式: {"type":"saveKeyValue","data":{"key":"isUseShangMi","value":false}},其中type用于判断不同的方法
  android/ios: XSDKFlutterFunction.postMessage(json);

  mac:
  if (window.webkit) {
      window.webkit.messageHandlers.XSDKFlutterFunction.postMessage(json);
  }
   */
  void _setXJSRunNativeFunctionChannel() {
    String commonFunctionName = 'XSDKFlutterFunction';

    if (XFlutterUtils.isAndroidOriOSSystem()
        || (isUseWebview_flutter_4_10_0 && XFlutterUtils.isMacOS())
    ) {
      /*
      webview_flutter
      flutter端:
      controller.addJavaScriptChannel(
        name: 'FlutterFunction1',
        onMessageReceived: (JavascriptMessage message) {
          print(message.message);
        },
      );
      web端: postMessage是固定的方法名, name为设置的'XSDKFlutterFunction'
      XSDKFlutterFunction.postMessage('Hello');
      注意:
      添加一个新的JavaScript通道只有在下一页加载生效后才生效,需要androidiOSWebViewController?.reload();或setState(() {}
      因此注意时间差,也可能不用重新加载url,但最好在加载url前调用;
       */
      androidiOSWebViewController?.addJavaScriptChannel(commonFunctionName,
          onMessageReceived: (JavaScriptMessage javaScriptMessage) {
            XLogUtils.printLog('---android/iOS js调用原生方法= ${javaScriptMessage.message}');
            //处理js调用原生方法
            handleReceiveJsSendMsg(javaScriptMessage.message);
          }
      );


    } else if ('Windows' == XFlutterUtils.getSystemName()) {
      /*
      webview_windows(通过web端发送消息至flutter端实现)
      flutter端(接收):
      _controller.webMessage.listen((event) {
        print(event);
      });
      web端(发送):
      window.chrome.webview.postMessage('信息')
       */
      windowWebViewController?.webMessage.listen((event) {
        XLogUtils.printLog('---windows js调用原生方法= $event');
        //处理js调用原生方法
        handleReceiveJsSendMsg(event);
      });
    }else{
      /*
      desktop_webview_window: 目前只支持mac:
      js端调用:
      if (window.webkit) {
                window.webkit.messageHandlers.XSDKFlutterFunction.postMessage(json);
            }
       */
      dWebview?.registerJavaScriptMessageHandler(
          commonFunctionName, //XSDKFlutterFunction
              (name, data) {
            //name为postMessage, data为json
            XLogUtils.printLog('---mac js调用原生方法= $name, $data');
            handleReceiveJsSendMsg(data);
          });
    }
  }

  /*
  处理js调用原生方法,接收json数据
  json格式: {"type":"saveKeyValue","data":{"key":"isUseShangMi","value":false}},其中type用于判断不同的方法
  web端调用(各自trycatche同时调用适配):
  (android/ios):  XSDKFlutterFunction.postMessage(json字符串);
  (window): window.chrome.webview.postMessage(json字符串)
  (mac):  if (window.webkit) {
             window.webkit.messageHandlers.XSDKFlutterFunction.postMessage(json);
          }
   */
  @required
  void handleReceiveJsSendMsg(dynamic json);

  /*
  webview加载失败通知外界,注意: 只支持android,iOS,windows, 不支持mac(api没有失败通知);
   */
  @required
  void onNotifyWebViewLoadUrlError(int? errorCode, String? description, String? failingUrl);

  //开始加载
  void onNotifyWebViewLoadStart(){

  }
  void onNotifyWebViewLoadEnd(){

  }

  //mac linux 弹新窗口////////////////////////////////////////////////////////////////////
  /*
webView.setBrightness：更改 webview 主题。仅适用于：macOS（Brightness.dark 仅 10.14+
webView.registerJavaScriptMessageHandler：注册可以从 Javascript 代码调用的消息处理程序。仅macOS
webView.setApplicationNameForUserAgent('FlutterDesk'); 将字符串附加到 webview 的用户代理
添加要在创建的文档上执行的脚本:
webView.addScriptToExecuteOnDocumentCreated('''
  window.onload = function() {
    console.log("Hello Flutter");
  }
''');
注册将在 webview 历史记录更改时调用的回调,检测当前页面是否可以使用左上角箭头前进后退一个页面。返回的是bool值
webView.setOnHistoryChangedCallback((canGoBack, canGoForward) {
  print('canGoBack: $canGoBack');
  print('canGoForward: $canGoForward');
});
监听URL请求回调,通过使用该方法我们可以获取一些回调信息
webView.addOnUrlRequestCallback((url) {
  _allUrl.add(url);
  setState(() {});
});
设置提示处理程序。仅macOS
webView.setPromptHandler((prompt, defaultText) {
        if (prompt == "test") {
          return "Hello World!";
        } else if (prompt == "init") {
          return "initial prompt";
        }
        return "";
      })
   */

  desktopWebview.Webview? dWebview;
  //
  Future<void> _initWebView_desktop_mac_linux() async {

    XLogUtils.printLog('---webview, mac屏幕宽度:${XTempData.screenWidth},高度:${XTempData.screenHeight}');

    dWebview = await desktopWebview.WebviewWindow.create(
      configuration: desktopWebview.CreateConfiguration(
        windowWidth: 1400,//XTempData.screenWidth.toInt(),
        windowHeight: 800,//XTempData.screenHeight.toInt(),
        title: desktopNewWindowTitle,
        titleBarHeight: 38, //macOS
        titleBarTopPadding:0,
        userDataFolderWindows: await _getDesktopWebViewPath(),
        openMaximized:true,
      ),
    );

    //设置js调用原生方法通用通道
    _setXJSRunNativeFunctionChannel();

    dWebview?.addOnUrlRequestCallback((String url) {
      XLogUtils.printLog("---mac webview url=$url");
      loadFailUrl=url;
    });
    dWebview?.addOnWebMessageReceivedCallback((String message) {
      XLogUtils.printLog("---mac webview message=$message");
    });

    //监听
    dWebview?.isNavigating.addListener(() {
      XLogUtils.printLog("---mac webview isNavigating=${dWebview?.isNavigating.value}");
      if(dWebview!.isNavigating.value){
        //正在加载
      }else{
        //加载完成
        hideLoading();
      }
    });

    //由于dWebview没有加载失败监听,无法通知外界,无法自定义失败提示界面,但默认失败界面自带有刷新按钮
    //dWebview?.reload();

    dWebview?.launch(
        getWebUrl() //创建一个WebView窗口
    );

    if(openDevTools){
      dWebview?.openDevToolsWindow();
    }
  }

  Future<void> _clearWebView_desktop_mac_linux() async {
    await dWebview?.stop();
    await desktopWebview.WebviewWindow.clearAll(
      userDataFolderWindows: await _getDesktopWebViewPath(),
    );
    dWebview?.close();
  }

  Future<String> _getDesktopWebViewPath() async {
    final document = await getApplicationDocumentsDirectory();
    return path.join(
      document.path,
      'desktop_webview_window',
    );
  }

  //windows////////////////////////////////////////////////////////

  WebviewController? windowWebViewController;

  Future<void> _initWebView_windows() async {
    try {

      if(windowWebViewController==null){
        windowWebViewController = WebviewController();

        await windowWebViewController?.initialize();


        //加载url监听(只监听1次)
        // windowWebViewController?.url.listen((url) {
        //   XLogUtils.printLog('---windowWebViewController listen url=$url');
        // });
        windowWebViewController?.url.asBroadcastStream().listen((String url) {
          loadFailUrl=url;
        });

        windowWebViewController?.containsFullScreenElementChanged.listen((flag) {
          windowManager.setFullScreen(flag);
        });


        await windowWebViewController?.setBackgroundColor(Colors.transparent);
        await windowWebViewController?.setPopupWindowPolicy(WebviewPopupWindowPolicy.deny);

        windowWebViewController?.onLoadError.asBroadcastStream().listen((WebErrorStatus event) {
          XLogUtils.printLog("---window webview error: ${event.toString()}");
          //置空白页
          reLoadUrl("about:blank");
          onNotifyWebViewLoadUrlError(-1,event.toString(),loadFailUrl);
        });

        windowWebViewController?.loadingState.asBroadcastStream().listen((LoadingState event) {
          XLogUtils.printLog("---window webview LoadingState: ${event.toString()}");
          if(event==LoadingState.navigationCompleted){
            //加载完毕
            hideLoading();
          }
        });

        //设置js调用原生方法通用通道
        _setXJSRunNativeFunctionChannel();

        if(openDevTools){
          windowWebViewController?.openDevTools();
        }

        await windowWebViewController?.loadUrl(getWebUrl());

        if (!mounted) return;
        setState(() {});

      }


    } catch (e) {
      XLogUtils.printLog('$e');
    }
  }

  Widget _createWindowsWebViewWidget() {
    return Webview(
      windowWebViewController!,
      permissionRequested: _windowsOnPermissionRequested,
    );
  }

  Future<void> _clearWebView_windows() async {
    await windowWebViewController?.stop();
    await windowWebViewController?.clearCache();
    await windowWebViewController?.clearCookies();
    await windowWebViewController?.dispose();
  }

  Future<WebviewPermissionDecision> _windowsOnPermissionRequested(
      String url, WebviewPermissionKind kind, bool isUserInitiated) async {
    final decision = await showDialog<WebviewPermissionDecision>(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        title: const Text('WebView permission requested'),
        content: Text('WebView has requested permission \'$kind\''),
        actions: <Widget>[
          TextButton(
            onPressed: () =>
                Navigator.pop(context, WebviewPermissionDecision.deny),
            child: const Text('Deny'),
          ),
          TextButton(
            onPressed: () =>
                Navigator.pop(context, WebviewPermissionDecision.allow),
            child: const Text('Allow'),
          ),
        ],
      ),
    );

    return decision ?? WebviewPermissionDecision.none;
  }

  //android ios//////////////////////////////////////////////////////////////


  WebViewController? androidiOSWebViewController;
  WebViewCookieManager? androidiOSCookieManager;

  void _initWebView_android_iOS() {

    //
    PlatformWebViewControllerCreationParams params;
    if (WebViewPlatform.instance is WebKitWebViewPlatform) {
      params = WebKitWebViewControllerCreationParams(
        allowsInlineMediaPlayback: false,
        mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
      );
    } else {
      params = const PlatformWebViewControllerCreationParams();
    }

    androidiOSWebViewController = WebViewController.fromPlatformCreationParams(params);

    if (androidiOSWebViewController?.platform is AndroidWebViewController) {
      if(openDevTools){
        AndroidWebViewController.enableDebugging(true);
      }else{
        AndroidWebViewController.enableDebugging(false);
      }

      (androidiOSWebViewController?.platform as AndroidWebViewController).setMediaPlaybackRequiresUserGesture(false);
    }

    //
    androidiOSWebViewController
      ?..setJavaScriptMode(JavaScriptMode.unrestricted) //开启js
      //..setBackgroundColor(const Color(0x00000000)) //实测mac平台不支持,编译报错
      ..enableZoom(false) //禁止双击放大
      ..setOnConsoleMessage((JavaScriptConsoleMessage message){
        //日志打印
        XLogUtils.printLog('---webview JavaScriptConsoleMessage: ${message.message}');
      })
      ..setNavigationDelegate(
        NavigationDelegate(
          onProgress: (int progress) {
            //加载进度
            //XLogUtils.printLog('---WebView is loading (progress : $progress%)');
          },
          onPageStarted: (String url) {
            XLogUtils.printLog('---webview, onPageStarted,url: $url');
            onNotifyWebViewLoadStart();
          },
          onPageFinished: (String url) async {
            //获取web页面的title
            //final String? title = await iOSAndroidWebViewController?.getTitle();
            //XLogUtils.printLog('---Page finished loading: $url, title=$title');
            hideLoading();
            onNotifyWebViewLoadEnd();
          },
          onWebResourceError: (error) {
            XLogUtils.printLog('---android/iOS webview error: code: ${error.errorCode}, description: ${error.description}, errorType: ${error.errorType.toString()}, isForMainFrame: ${error.isForMainFrame}, url=${error.url}');

            if(true==error.isForMainFrame){
              //加载失败的url
              loadFailUrl=error.url;
              //置空白页
              reLoadUrl("about:blank");

              onNotifyWebViewLoadUrlError(error.errorCode,error.description,error.url);
            }

          },
          onNavigationRequest: (NavigationRequest request) {
            XLogUtils.printLog('---webview NavigationRequest: ${request.url}');
            if (request.url.startsWith('https://www.youtube.com/')) {
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
          onUrlChange: (UrlChange change){
            XLogUtils.printLog('---webview UrlChange: request:uri=${change.url}');
          },
          //认证
          // onHttpAuthRequest: (HttpAuthRequest request) {
          //   _openDialog(request);
          // },
        ),
      );

    //设置js调用原生方法通用通道
    _setXJSRunNativeFunctionChannel();

    //cookies管理
    PlatformWebViewCookieManagerCreationParams cookieParams = const PlatformWebViewCookieManagerCreationParams();
    if (WebViewPlatform.instance is WebKitWebViewPlatform) {
      cookieParams = WebKitWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams(cookieParams);
    } else if (WebViewPlatform.instance is AndroidWebViewPlatform) {
      cookieParams = AndroidWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams(cookieParams);
    }
    androidiOSCookieManager = WebViewCookieManager.fromPlatformCreationParams(cookieParams);

    /*
    第二种:
    androidiOSCookieManager = WebViewCookieManager();
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
  final WebKitWebViewCookieManager webKitManager =
      cookieManager.platform as WebKitWebViewCookieManager;
} else if (WebViewPlatform.instance is AndroidWebViewPlatform) {
  final AndroidWebViewCookieManager androidManager =
      cookieManager.platform as AndroidWebViewCookieManager;
}
     */

    if(isCleanAllCookies){
      //清除所有已存在的cookies

      //注意:要在在loadRequest之前调用setCookie
      androidiOSCookieManager!.clearCookies().then((value){
        //适配iOS,要清除旧cookie再重新设置,否则设置cookie失败

        if(cookiesMap!=null){

          String domain=cookiesMap!['domain']??'';
          Map<String,dynamic> cookies=cookiesMap!['cookies']??{};
          //遍历
          cookies.forEach(
                  (key, value)
              {
                //print("forEach 遍历 : $key : $value");
                androidiOSCookieManager!.setCookie(
                    WebViewCookie(
                        name: key,
                        value: '$value',
                        domain: domain
                    )
                );
              }
          );

        }

        //可添加headers
        androidiOSWebViewController?.loadRequest(Uri.parse(getWebUrl()));
      });

    }else{
      //不清除已有cookies

      if(cookiesMap!=null){

        String domain=cookiesMap!['domain']??'';
        Map<String,dynamic> cookies=cookiesMap!['cookies']??{};
        //遍历
        cookies.forEach(
                (key, value)
            {
              //print("forEach 遍历 : $key : $value");
              androidiOSCookieManager!.setCookie(
                  WebViewCookie(
                      name: key,
                      value: '$value',
                      domain: domain
                  )
              );
            }
        );

      }

      //可添加headers
      if(!XStringUtils.isEmpty(loadHtmlText)){
        androidiOSWebViewController?.loadHtmlString(loadHtmlText!);
      }else{
        androidiOSWebViewController?.loadRequest(Uri.parse(getWebUrl()));
      }
    }

  }

  Future<void> _clearWebView_android_iOS() async {
    await androidiOSWebViewController?.clearCache();
    await androidiOSWebViewController?.clearLocalStorage();
    await androidiOSCookieManager?.clearCookies();
  }



  // Future<void> _openDialog(HttpAuthRequest httpRequest) async {
  //   final TextEditingController usernameTextController =
  //   TextEditingController();
  //   final TextEditingController passwordTextController =
  //   TextEditingController();
  //
  //   return showDialog(
  //     context: context,
  //     barrierDismissible: false,
  //     builder: (BuildContext context) {
  //       return AlertDialog(
  //         title: Text('${httpRequest.host}: ${httpRequest.realm ?? '-'}'),
  //         content: SingleChildScrollView(
  //           child: Column(
  //             mainAxisSize: MainAxisSize.min,
  //             children: <Widget>[
  //               TextField(
  //                 decoration: const InputDecoration(labelText: 'Username'),
  //                 autofocus: true,
  //                 controller: usernameTextController,
  //               ),
  //               TextField(
  //                 decoration: const InputDecoration(labelText: 'Password'),
  //                 controller: passwordTextController,
  //               ),
  //             ],
  //           ),
  //         ),
  //         actions: <Widget>[
  //           // Explicitly cancel the request on iOS as the OS does not emit new
  //           // requests when a previous request is pending.
  //           TextButton(
  //             onPressed: () {
  //               httpRequest.onCancel();
  //               Navigator.of(context).pop();
  //             },
  //             child: const Text('Cancel'),
  //           ),
  //           TextButton(
  //             onPressed: () {
  //               httpRequest.onProceed(
  //                 WebViewCredential(
  //                   user: usernameTextController.text,
  //                   password: passwordTextController.text,
  //                 ),
  //               );
  //               Navigator.of(context).pop();
  //             },
  //             child: const Text('Authenticate'),
  //           ),
  //         ],
  //       );
  //     },
  //   );
  // }

  //////////////////////////////////////////////////////

  //重新加载失败的url
  String? loadFailUrl;
  void reLoadUrl(String? url){
    if(!XStringUtils.isEmpty(url)){
      //android.iOS
      androidiOSWebViewController?.loadRequest(Uri.parse(url!));
      //inAppWebViewController?.loadUrl(urlRequest: URLRequest(url:Uri.parse(url!)));
      //windows
      windowWebViewController?.loadUrl(url!);
      //mac 会创建一个新的WebView窗口
      //dWebview?.launch(url!);
    }
  }


  Widget? webViewWidget;

  //InAppWebViewController? inAppWebViewController;

  //各个平台的webview
  Widget _getWebviewWidget(){
    if (XFlutterUtils.isAndroidOriOSSystem()
        || (isUseWebview_flutter_4_10_0 && XFlutterUtils.isMacOS())
    ) {

      webViewWidget ??= WebViewWidget(
        controller: androidiOSWebViewController!,
        // gestureRecognizers:<Factory<OneSequenceGestureRecognizer>>{
        //   Factory<OneSequenceGestureRecognizer>(()=>EagerGestureRecognizer()),
        // }.toSet()
      );


    } else if ('Windows' == XFlutterUtils.getSystemName()) {
      //windows
      XLogUtils.printLog('---_createWindowsWebViewWidget');
      webViewWidget = _createWindowsWebViewWidget();
    }else{
      //mac,linux 弹新窗口
      webViewWidget ??= XWidgetUtils.getLayoutColumn(
          children: [
            XWidgetUtils.getLayoutExpanded(
                XWidgetUtils.getLayoutContainer(
                    alignment: Alignment.bottomCenter,
                    child:desktopIndexPageCenterWidget!=null?desktopIndexPageCenterWidget!:XWidgetUtils.getLayoutContainer()
                )
            ),
            XWidgetUtils.getLayoutContainer(height: 38),//中间此高度为webview弹窗标题栏的高度(背景为此widget,因此在这里留白)
            XWidgetUtils.getLayoutExpanded(XWidgetUtils.getLayoutContainer()),
          ]
      );
    }

    return webViewWidget!;
  }

  /// Adds a new JavaScript channel to the set of enabled channels.
  ///
  /// The JavaScript code can then call `postMessage` on that object to send a
  /// message that will be passed to [onMessageReceived].
  ///
  /// For example, after adding the following JavaScript channel:
  ///
  /// ```dart
  /// final WebViewController controller = WebViewController();
  /// controller.addJavaScriptChannel(
  ///   name: 'Print',
  ///   onMessageReceived: (JavascriptMessage message) {
  ///     print(message.message);
  ///   },
  /// );
  /// ```
  ///
  /// JavaScript code can call:
  ///
  /// ```javascript
  /// Print.postMessage('Hello');
  /// ```
  ///
  /// to asynchronously invoke the message handler which will print the message
  /// to standard output.
  ///
  /// Adding a new JavaScript channel only takes affect after the next page is
  /// loaded.
  ///
  /// A channel [name] cannot be the same for multiple channels.
  ///
/*
  js调原生
  webViewController.addJavaScriptChannel(
        'Toaster',
        onMessageReceived: (JavaScriptMessage message) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(message.message)),
          );
        },
      )

  原生调js
  Future<void> _onShowUserAgent() {
    // Send a message with the user agent string to the Toaster JavaScript channel we registered
    // with the WebView.
    return webViewController.runJavaScript(
      'Toaster.postMessage("User Agent: " + navigator.userAgent);',
    );
  }

  Future<void> _onListCookies(BuildContext context) async {
    final String cookies = await webViewController
        .runJavaScriptReturningResult('document.cookie') as String;
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          const Text('Cookies:'),
          _getCookieList(cookies),
        ],
      ),
    ));
  }

  Future<void> _onAddToCache(BuildContext context) async {
    await webViewController.runJavaScript(
      'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";',
    );
    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
      content: Text('Added a test entry to cache.'),
    ));
  }

  Future<void> _onListCache() {
    return webViewController.runJavaScript('caches.keys()'
        // ignore: missing_whitespace_between_adjacent_strings
        '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
        '.then((caches) => Toaster.postMessage(caches))');
  }



  Future<void> _onNavigationDelegateExample() {
    final String contentBase64 = base64Encode(
      const Utf8Encoder().convert(kNavigationExamplePage),
    );
    return webViewController.loadRequest(
      Uri.parse('data:text/html;base64,$contentBase64'),
    );
  }

  Future<void> _onSetCookie() async {
    await cookieManager.setCookie(
      const WebViewCookie(
        name: 'foo',
        value: 'bar',
        domain: 'httpbin.org',
        path: '/anything',
      ),
    );
    await webViewController.loadRequest(Uri.parse(
      'https://httpbin.org/anything',
    ));
  }

  Future<void> _onDoPostRequest() {
    return webViewController.loadRequest(
      Uri.parse('https://httpbin.org/post'),
      method: LoadRequestMethod.post,
      headers: <String, String>{'foo': 'bar', 'Content-Type': 'text/plain'},
      body: Uint8List.fromList('Test Body'.codeUnits),
    );
  }

  Future<void> _onLoadLocalFileExample() async {
    final String pathToIndex = await _prepareLocalFile();
    await webViewController.loadFile(pathToIndex);
  }

  Future<void> _onLoadFlutterAssetExample() {
    return webViewController.loadFlutterAsset('assets/www/index.html');
  }

  Future<void> _onLoadHtmlStringExample() {
    return webViewController.loadHtmlString(kLocalExamplePage);
  }

  Future<void> _onTransparentBackground() {
    return webViewController.loadHtmlString(kTransparentBackgroundPage);
  }

  Widget _getCookieList(String cookies) {
    if (cookies == null || cookies == '""') {
      return Container();
    }
    final List<String> cookieList = cookies.split(';');
    final Iterable<Text> cookieWidgets =
        cookieList.map((String cookie) => Text(cookie));
    return Column(
      mainAxisAlignment: MainAxisAlignment.end,
      mainAxisSize: MainAxisSize.min,
      children: cookieWidgets.toList(),
    );
  }

  static Future<String> _prepareLocalFile() async {
    final String tmpDir = (await getTemporaryDirectory()).path;
    final File indexFile = File(
        <String>{tmpDir, 'www', 'index.html'}.join(Platform.pathSeparator));

    await indexFile.create(recursive: true);
    await indexFile.writeAsString(kLocalExamplePage);

    return indexFile.path;
  }

  if (await webViewController.canGoBack()) {
      await webViewController.goBack();
  }

  if (await webViewController.canGoForward()) {
      await webViewController.goForward();
  }

  webViewController.reload(),

   */


  //返回上页面
  void goBack(){
    androidiOSWebViewController?.goBack();
    windowWebViewController?.goBack();
    //dWebview?.back();
    //inAppWebViewController?.goBack();
  }


}
